using System;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using CodePlex.DnD4eCampaignManager.DnD4e;
using CodePlex.DnD4eCampaignManager.DnD4e.Enums;
using CodePlex.DnD4eCampaignManager.Services.Interfaces;
using WatiN.Core;

namespace CodePlex.DnD4eCampaignManager.Services
{
    public class DungeonsAndDragonsInsiderMonsterImporter : IDungeonsAndDragonsInsiderMonsterImporter
    {
        private const RegexOptions REGEX_OPTIONS = RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline;
        private readonly string _dungeonsAndDragonsInsiderPassword;
        private readonly string _dungeonsAndDragonsInsiderUserName;

        public DungeonsAndDragonsInsiderMonsterImporter(string dungeonsAndDragonsInsiderUserName, string dungeonsAndDragonsInsiderPassword)
        {
            _dungeonsAndDragonsInsiderUserName = dungeonsAndDragonsInsiderUserName;
            _dungeonsAndDragonsInsiderPassword = dungeonsAndDragonsInsiderPassword;
        }

        public Monster ImportMonster(int monsterId)
        {
            try
            {
                string monsterText = GetMonsterText(monsterId);
                return GetMonsterFromHtmlString(monsterText);
            }
            catch ( Exception ex )
            {
                Debug.WriteLine(ex.Message);
                return null;
            }
        }

        private string GetMonsterText(int monsterId)
        {
            using ( IE browser = GetBrowser() )
            {
                browser.GoTo("http://www.wizards.com/dndinsider/compendium/monster.aspx?id=" + monsterId);
                if ( browser.TextFields.Filter(x => x.Id == "email").Length == 1 )
                {
                    TextField usernameText = browser.TextField(x => x.Id == "email");
                    usernameText.TypeText(_dungeonsAndDragonsInsiderUserName);
                    TextField passwordText = browser.TextField("password");
                    passwordText.TypeText(_dungeonsAndDragonsInsiderPassword);
                    Button loginButton = browser.Button("InsiderSignin");
                    loginButton.Click();
                }
                return browser.Html;
            }
        }

        private static string GetAttacksFromMonsterHtml(string monsterHtml)
        {
            MatchCollection matches = Regex.Matches(monsterHtml,
                                                    @"<P class=""flavor alt""><IMG .*?<B>(?<PowerName>[\w\s]*?)</B> \((?<Type>.*?)\).*?<B>(?<Keywords>.*?)</B>.*?</P>.*?<P class=flavorIndent>(?<Effect>.*?)</P>",
                                                    REGEX_OPTIONS);
            StringBuilder powers = new StringBuilder();
            foreach ( Match match in matches )
            {
                string name = match.Groups["PowerName"].Value;
                string type = Regex.Replace(match.Groups["Type"].Value, @"<IMG.*?(\d).+?gif"">", "$1 ").Trim().Replace("recharge", "Recharge:");
                string keywords = match.Groups["Keywords"].Value;
                string effect = match.Groups["Effect"].Value;

                powers.AppendFormat("{1} ({2}, {3}){0}{4}{0}{0}", Environment.NewLine, name, type, keywords, effect);
            }
            return powers.ToString().Trim();
        }

        private static IE GetBrowser()
        {
            Settings.Instance.MakeNewIeInstanceVisible = false;
            Settings.Instance.AutoMoveMousePointerToTopLeft = false;
            Settings.Instance.AutoCloseDialogs = true;
            return new IE(false);
        }

        private static string GetHighlightedNumericValueFromMonsterHtmlAsString(string monsterText, string keyword)
        {
            return GetMatchGroupValueFromPattern(monsterText, string.Format(@"<B>{0}</B>.+?(?<Value>[\+-]{{0,1}}\d+)", keyword), "Value");
        }

        private static string GetMatchGroupValueFromPattern(string monsterText, string matchPattern, string groupName)
        {
            Group matchGroupValueFromPattern = Regex.Match(monsterText, matchPattern, REGEX_OPTIONS).Groups[groupName];
            return matchGroupValueFromPattern.Success ? matchGroupValueFromPattern.Value : string.Empty;
        }


        private static Monster GetMonsterFromHtmlString(string monsterHtml)
        {
            Monster monster = new Monster(new Abilities(), new Defenses(), new HitPoints(), new Movement(), new Senses(), new Initiative());
            monster = SetMonsterHeaderInfoFromHtml(monster, monsterHtml);

            monster.AbilityStrength = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Str").TryParseToInt();
            monster.AbilityDexterity = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Dex").TryParseToInt();
            monster.AbilityConstitution = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Con").TryParseToInt();
            monster.AbilityIntelligence = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Int").TryParseToInt();
            monster.AbilityWisdom = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Wis").TryParseToInt();
            monster.AbilityCharisma = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Cha").TryParseToInt();

            monster.MovementStealth =
                GetMatchGroupValueFromPattern(monsterHtml, string.Format(@"{0}.+?(?<Value>[\+-]{{0,1}}\d+)", "Stealth"), "Value").TryParseToInt();

            monster.SensesPerception = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Senses").TryParseToInt();
            monster.SensesOther = GetMatchGroupValueFromPattern(monsterHtml, @"<B>Senses</B> Perception.+?\d+; (?<Senses>.+?)<BR>", "Senses");
            monster.InitiativeRoll = 1;
            monster.InitiativeModifier = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Initiative").TryParseToInt();
            monster.HitPointsMaximum = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "HP").TryParseToInt();
            monster.DefenseArmorClass = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "AC").TryParseToInt();
            monster.DefenseFortitude = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Fortitude").TryParseToInt();
            monster.DefenseReflex = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Reflex").TryParseToInt();
            monster.DefenseWillpower = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Will").TryParseToInt();
            monster.MovementSpeed = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Speed").TryParseToInt();
            monster.MovementSpeedOther = GetMatchGroupValueFromPattern(monsterHtml, @"<B>Speed</B> \d+(?<OtherMovement>.*?)<", "OtherMovement");
            monster.SavingThrows = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Saving Throws").TryParseToInt();
            monster.ActionPoints = GetHighlightedNumericValueFromMonsterHtmlAsString(monsterHtml, "Action Points").TryParseToInt();

            monster.AttackPowers = GetAttacksFromMonsterHtml(monsterHtml);
            monster.SpecialQualities = GetSpecialQualitiesFromMonsterHtml(monsterHtml);
            return monster;
        }

        private static string GetSpecialQualitiesFromMonsterHtml(string monsterHtml)
        {
            MatchCollection matches = Regex.Matches(monsterHtml,
                                                    "<P class=\"flavor alt\"><B>(?<Name>.*?)</B>(?<Trigger>.*?)</P>.*?<P class=flavorIndent>(?<Description>.*?)</P>",
                                                    REGEX_OPTIONS);
            StringBuilder powers = new StringBuilder();
            foreach ( Match match in matches )
            {
                string name = match.Groups["Name"].Value;
                string trigger = match.Groups["Trigger"].Value.Trim();
                string description = match.Groups["Description"].Value.Trim();
                powers.AppendFormat("{0} {1}", name, trigger);
                powers.AppendLine();
                powers.AppendFormat("{0}", description);
                powers.AppendLine();
                powers.AppendLine();
            }
            return powers.ToString().Trim();
        }

        private static string MakeFileNameSafe(string fileName)
        {
            return Regex.Replace(fileName, "[\\/?*:<>|\"]", string.Empty);
        }

        private static string RemoveMarkUp(string equipment)
        {
            return Regex.Replace(equipment, "<.*?>", string.Empty).Replace(".", string.Empty).Trim();
        }

        private static Monster SetMonsterHeaderInfoFromHtml(Monster monster, string monsterText)
        {
            //@"<H1 class=monster><SPAN class=level>Level (?<Level>\d+) (?<Role>.+)<SPAN class=xp>.*</SPAN>(?<Name>.*)<SPAN class=type>(?<Size>.+?) (?<Keywords>.*)</SPAN>.*?</H1>";
            monster.Name = MakeFileNameSafe(GetMatchGroupValueFromPattern(monsterText, "<H1 class=monster>(?<Name>.*?)<", "Name"));
            monster.Level = int.Parse(GetMatchGroupValueFromPattern(monsterText, "<SPAN class=level>Level (?<Level>\\d+) ", "Level"));
            string role =
                RemoveMarkUp(
                    GetMatchGroupValueFromPattern(monsterText, "<SPAN class=level>Level \\d+ (?<Role>.+?)<", "Role").Replace("Elite", string.Empty).
                        Replace("(Leader)", string.Empty).Replace("Solo", string.Empty));
            MonsterRole monsterRole = MonsterRole.Soldier;
            if ( Enum.IsDefined(typeof ( MonsterRole ), role) )
            {
                monsterRole = (MonsterRole) Enum.Parse(typeof ( MonsterRole ), role.Split(' ')[0]);
            }
            monster.Role = monsterRole;
            CreatureSize creatureSize = CreatureSize.Medium;
            string creatureSizeString = GetMatchGroupValueFromPattern(monsterText, "<SPAN class=type>(?<Size>.+?) .*?<", "Size");
            if ( Enum.IsDefined(typeof ( CreatureSize ), creatureSizeString) )
            {
                creatureSize = (CreatureSize) Enum.Parse(typeof ( CreatureSize ), creatureSizeString);
            }
            monster.CreatureSize = creatureSize;
            monster.Origin =
                Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(
                    GetMatchGroupValueFromPattern(monsterText, "<SPAN class=type>.+? (?<Origin>.*?) .*?<", "Origin").Trim());
            monster.Keywords =
                GetMatchGroupValueFromPattern(monsterText, "<SPAN class=type>.+? .+? (?<Keywords>.*?)<", "Keywords").Trim().ToListOfStrings();
            monster.Elite = Regex.IsMatch(monsterText, "<H1.*Elite.*</H1>", REGEX_OPTIONS);
            monster.Leader = Regex.IsMatch(monsterText, "<H1.*Leader.*</H1>", REGEX_OPTIONS);
            monster.Solo = Regex.IsMatch(monsterText, "<H1.*Solo.*</H1>", REGEX_OPTIONS);
            string equipment = GetMatchGroupValueFromPattern(monsterText, "<B>Equipment</B>: (?<Equipment>.*?)</P>", "Equipment");
            monster.Equipment = RemoveMarkUp(equipment);
            return monster;
        }
    }
}